<template>
  <q-page class="q-pa-md">
    <searchArea :fieldlist="searchList" @search="search" @reset="search" />
    <q-table
      title="操作日志"
      :grid="$q.screen.xs"
      :rows="data"
      :columns="columns"
      row-key="id"
      v-model:pagination="pagination"
      :loading="loading"
      :filter="filter"
      :rows-per-page-options="[25, 50, 100, 0]"
      @request="onRequest"
      binary-state-sort
      :style="$q.screen.xs ? '' : 'max-height: calc(100vh - 100px)'"
      class="my-sticky-virtscroll-table"
      virtual-scroll
    >
      <template v-slot:top>
        <div class="q-gutter-md full-width flex">
          <div class="q-table__title">操作日志</div>
          <q-space />
          <q-btn round flat icon="settings" @click="settings" />
        </div>
      </template>

      <template v-slot:body-cell-oper_content="props">
        <q-td class="q-gutter-sm" :props="props">
          {{props.row.oper_content && props.row.oper_content.length > 40 ? props.row.oper_content.substr(0, 40) + '...' : props.row.oper_content}}
          <q-btn v-if="props.row.oper_content && props.row.oper_content.length>40" round icon="search" size="sm" @click="viewDetail(props.row)">
            <q-tooltip>查看详细数据</q-tooltip>
          </q-btn>
        </q-td>
      </template>

      <template v-slot:item="props">
        <q-list dense class="fit shadow-1 q-mb-md q-py-md">
          <q-item v-for="col in props.cols" :key="col.name">
            <q-item-section class="text-grey-8">
              <q-item-label>{{ col.label }}</q-item-label>
            </q-item-section>
            <q-item-section>
              <q-item-label v-if="col.name=='oper_content'">
                {{ col.value.length>40?col.value.substr(0, 40)+'...':col.value }}
                <q-btn v-if="props.row.oper_content && props.row.oper_content.length>40" round icon="search" size="sm" @click="viewDetail(props.row)">
                  <q-tooltip>查看详细数据</q-tooltip>
                </q-btn>
              </q-item-label>
              <q-item-label v-else>{{ col.value }}</q-item-label>
            </q-item-section>
          </q-item>
        </q-list>
      </template>
    </q-table>

    <q-dialog v-model="alert">
      <q-card>
        <q-card-section>
          <div class="text-h6">操作内容</div>
        </q-card-section>

        <q-card-section class="q-pt-none">
          <pre  v-html="content" />
        </q-card-section>

        <q-card-actions align="right">
          <q-btn flat label="确定" color="blue-7" v-close-popup />
        </q-card-actions>
      </q-card>
    </q-dialog>
  </q-page>
</template>

<script>
import { defineComponent } from 'vue';
import { date, useMeta } from 'quasar';
import searchArea from 'src/components/SearchArea.vue';
import { useStore } from 'stores/store';
import { mapState } from 'pinia';

export default defineComponent({
  components: {
    searchArea
  },
  inject: ['inputBox'],
  setup (props, context) {
    const $store = useStore();
    useMeta({
      title: `操作日志 - ${$store.siteConfig.appName} - 木多鱼系列`
    });
  },
  data () {
    const self = this;
    return {
      filter: null,
      loading: false,
      pagination: {
        sortBy: 'id',
        descending: true,
        page: 1,
        rowsPerPage: 25,
        rowsNumber: 0
      },
      columns: [
        { name: 'create_time', label: '时间', field: 'create_time', sortable: true, align: 'center', format: val => date.formatDate(val * 1000, 'YYYY/MM/DD HH:mm:ss') },
        { name: 'user_name', label: '操作者', field: 'user_name', sortable: true, align: 'center' },
        { name: 'oper_model', label: '模块', field: 'oper_model', sortable: true, align: 'left' },
        { name: 'oper_type', label: '类型', field: 'oper_type', sortable: true, align: 'center', format: val => self.type[val] },
        { name: 'oper_content', label: '内容', field: 'oper_content', align: 'left' },
        { name: 'ipgeo', label: 'IP地址', field: 'ipgeo', align: 'center' },
        { name: 'browser', label: '浏览器', field: 'browser', align: 'left' },
        { name: 'os', label: '操作系统', field: 'os', align: 'center' }
      ],
      data: [],
      searchList: {
        tenant_id: {
          type: 'select',
          label: '租户',
          value: '',
          selected: [],
          options: [{ value: '0', label: '系统管理' }]
        },
        create_time: {
          type: 'date',
          label: '日期',
          value: ''
        },
        user_id: {
          type: 'select',
          label: '操作者',
          value: '',
          options: []
        },
        oper_type: {
          type: 'select',
          label: '类型',
          value: '',
          options: [
            { value: 'login', label: '登录' },
            { value: 'logout', label: '登出' },
            { value: 'add', label: '新增' },
            { value: 'edit', label: '修改' },
            { value: 'delete', label: '删除' },
            { value: 'sync', label: '同步' },
            { value: 'approve', label: '审批' },
            { value: 'cancel', label: '取消' },
            { value: 'check', label: '验收' },
            { value: 'import', label: '导入' },
            { value: 'export', label: '导出' },
            { value: 'other', label: '其它' }
          ]
        },
        oper_model: {
          type: 'select',
          label: '模块',
          value: '',
          options: []
        },
        oper_content: {
          type: 'input',
          label: '操作内容',
          value: ''
        }
      },
      alert: false,
      content: ''
    }
  },
  created () {
    if (this.userInfo.tenant_id > 0) { // 租户用户不可选择租户
      this.searchList.tenant_id.type = '';
    } else { // 系统管理加载租户列表
      this.$axios.post('/c/system/getDataDict', { types: 'tenant' }).then(({ data }) => {
        this.searchList.tenant_id.options.push(...data.tenant);
      });
    }
    this.searchList.tenant_id.value = `${this.userInfo.tenant_id}`;
  },
  mounted () {
    // get initial data from server (1st page)
    this.onRequest({
      pagination: this.pagination,
      filter: this.filter
    });

    this.$axios.get('/c2/manager/log/getOperModel').then(res => {
      this.searchList.oper_model.options = res.data.map(v => {
        return {
          value: v.oper_model,
          label: v.oper_model
        }
      });
    });
  },
  methods: {
    onRequest (props) {
      const { page, rowsPerPage, sortBy, descending } = props.pagination
      const filter = {};
      for (const field in this.searchList) {
        filter[field] = this.searchList[field].value
      }

      this.loading = true

      const startRow = (page - 1) * rowsPerPage;
      const count = rowsPerPage;
      this.$axios.post('/c2/manager/log/logsList', { startRow, count, filter, sortBy, descending })
        .then(res => {
          this.pagination.rowsNumber = res.data.rowsNumber;
          this.data = res.data.data;
          this.pagination.page != page && window.scrollTo(0, 0);// 回到顶部
          this.pagination.page = page;
          this.pagination.rowsPerPage = rowsPerPage;
          this.pagination.sortBy = sortBy;
          this.pagination.descending = descending;
          this.loading = false;
        });
    },
    search () {
      this.onRequest({
        pagination: this.pagination
      })
    },
    syntaxHighlight (json) {
      if (typeof json != 'string') {
        json = JSON.stringify(json, undefined, 2);
      }
      json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
      // eslint-disable-next-line no-useless-escape
      return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
        let cls = 'number';
        if (/^"/.test(match)) {
          if (/:$/.test(match)) {
            cls = 'key';
          } else {
            cls = 'string';
          }
        } else if (/true|false/.test(match)) {
          cls = 'boolean';
        } else if (/null/.test(match)) {
          cls = 'null';
        }
        return '<span class="' + cls + '">' + match + '</span>';
      });
    },
    viewDetail (row) {
      this.content = this.syntaxHighlight(row.oper_content);
      this.alert = true;
    },
    settings () {
      const self = this;
      this.$axios.get('/c/system/getConfig', { params: { key: 'logs' } }).then(({ data }) => {
        self.inputBox('超期清除日志(天)', data.expireDays).then(ret => {
          self.$axios.post('/c2/manager/log/updateLogsExpireDays', { days: ret })
        });
      });
    }
  },
  computed: {
    ...mapState(useStore, ['userInfo']),
    type () {
      return this.searchList.oper_type.options.reduce((a, b) => {
        a[b.value] = b.label;
        return a;
      }, {});
    }
  },
  watch: {
    'searchList.tenant_id.value' (val) {
      this.searchList.user_id.options = [];
      this.searchList.user_id.selected = [];
      this.searchList.user_id.value = '';
      if (val) {
        this.$axios.post('/c2/manager/user/userList', { filter: { tenant_id: val } }).then(({ data }) => {
          this.searchList.user_id.options = data.data.map(item => ({ label: item.nickname, value: item.id }));
          this.searchList.user_id.options = data.data.map(item => ({ label: item.nickname, value: item.id }));
        });
      }
    }
  }
});
</script>
<style>
pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
    .string { color: green; }
    .number { color: darkorange; }
    .boolean { color: blue; }
    .null { color: magenta; }
    .key { color: red; }
</style>
